//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2003-2007
// Magellan and MapSend are registered trademarks of Thales Navigation.
//
// Magellan MapSend TRK tracks parser.
//

# include "StdAfx.h"
# include "MapSendFile.h"
# include "Map.h"
# include "Types.h"
# include "Load.h"
# include "Log.h"
# include "Status.h"
# include "ParseHelpers.h"
# include "Wrappers.h"
# include "Globals.h"

class CMapSendTrkLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);
};

DECLARE_WPTRK_LOADER (CMapSendTrkLoader, "mapsend-trk", "trk", "Magellan MapSend tracks (*.trk)", ftiTracks);

bool CMapSendTrkLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	if (_cFileSize < sizeof (CMapSendHdr))
		return false;

	const CMapSendHdr * const pHdr = reinterpret_cast<const CMapSendHdr *> (_pData);

	if (
		::strncmp (pHdr->strSignature, c_strMapSendSignature30, sizeof (pHdr->strSignature)) != 0 &&
		::strncmp (pHdr->strSignature, c_strMapSendSignature34, sizeof (pHdr->strSignature)) != 0 &&
		::strncmp (pHdr->strSignature, c_strMapSendSignature36, sizeof (pHdr->strSignature)) != 0
	)
		return false;
	if (pHdr->dwType != mstTrack)
		return false;

	return true;
}

LoadStatus_t CMapSendTrkLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	const char * const pEnd  = _pData + _cFileSize;

	file_refs_t file_refs;
	file_refs.push_back (file_ref_t ());
	file_refs.back ().strFileName = _strFileName;
	file_refs.back ().bAttachment = true;

	SetProgressStatus (0);

	// Detect format version.
	const CMapSendHdr * const pHdr = reinterpret_cast<const CMapSendHdr *> (_pData);
	const size_t cVer = (pHdr->strVersion [0] - '0')*10 + (pHdr->strVersion [1] - '0');

	const bool bAltitudeIsFloat = cVer >= 36;
	const bool bHasCentiseconds = cVer >= 34;
	const size_t cTrackPointSize = bHasCentiseconds ? sizeof (CMapSendTrackPoint2) : sizeof (CMapSendTrackPoint);

	ReportText ("Format version: %d", cVer);

	for (const char * pData = _pData + sizeof (CMapSendHdr); pData < pEnd;) {
		const char * const pTrkBegin = pData;

		const BYTE btNameLen = * reinterpret_cast<const BYTE *> (pData);
		const char * const strName = pData + 1;

		const DWORD dwPoints = * reinterpret_cast<const DWORD *> (pData + 1 + btNameLen);
		const CMapSendTrackPoint  * const pPoints  = reinterpret_cast<const CMapSendTrackPoint *> (pData + 1 + btNameLen + sizeof (DWORD));
		const CMapSendTrackPoint2 * const pPoints2 = static_cast<const CMapSendTrackPoint2 *> (pPoints);

		pData = reinterpret_cast<const char *> (pPoints) + (cTrackPointSize)*dwPoints;

		points_t points;
		points.reserve (dwPoints);
		trackpoints_t extra_points;
		extra_points.reserve (dwPoints);
		for (size_t cPoint = 0; cPoint < dwPoints; ++ cPoint) {
			const CMapSendTrackPoint & pt = bHasCentiseconds ? static_cast<const CMapSendTrackPoint &> (pPoints2 [cPoint]) : pPoints [cPoint];
			if (pt.dwValid) {
				// Position.
				points.push_back (point_t (pt.dblX, -pt.dblY));

				// Extra data.
				extra_points.push_back (trackpoint_info_t ());
				trackpoint_info_t & extra = extra_points.back ();
				extra.wFlags = trackpoint_info_t::defAltitude | trackpoint_info_t::defTime;

				// Altitude.
				extra.fAltitude = bAltitudeIsFloat ? pt.fAltitude : pt.lAltitude;

				// Time.
				extra.dtTime = DWORD (pt.tmTime)/24./60./60. + 25569.;
				if (bHasCentiseconds) {
					extra.wFlags |= trackpoint_info_t::defTimeMilliseconds;
					extra.dtTime += pPoints2 [cPoint].btCentiseconds/24./60./60./100;
				}
			}
		}

		//
		// Create track.
		//
		rgn_t & rgn = _pMap->CreateRgn (rkPolyline);
		rgn.SetTrackType ();

		rgn.pFileRef = & file_refs.back ();
		rgn.cBeginPos = pTrkBegin - _pData;
		rgn.cEndPos = pEnd - _pData;
		rgn.cFileLine = -1;

		rgn.strLabel.assign (strName, btNameLen);

		rgn.elements.push_back (rgn_element_t ());
		rgn_element_t & element = rgn.elements.back ();
		element.cLevel = 0;
		element.cFileOffset = rgn.cBeginPos;

		element.points.swap (points);

		track_info_t * const pTrkExtra = rgn.pExtra->wtr.CreateTrack ();
		element.pExtra->TrackPoints.swap (extra_points);

		SetProgressStatus (50*(pData - _pData)/_cFileSize);

		break;
		// NOTE: there are some undocumented 0x30 bytes in the end of file (36 format).
	}

	_pMap->file_refs.swap (file_refs);

	// Suppress warning about empty Zoom etc.
	_pMap->bNoHeader = true;
	_pMap->cHeaderLen = sizeof (CMapSendHdr);

	SetProgressStatus (50);

	return lsOK;
}
